extends Reference

class_name SimpleGeometry

var _vertices: Array = []
var _halfedges: Array = []
var _faces: Array = []

class Vertex:
	extends Object

	# position
	var pos: Vector3 = Vector3(0, 0, 0)
	# smoothed normal
	var smoothed_normal: Vector3 = Vector3(0, 0, 0)
	# an outgoing halfedge
	var edge = null

	var tmp: int = 0

class Halfedge:
	extends Object

	# the vertex it points to
	var vertex: Vertex = null
	# the face it belongs to
	var face = null
	# the next halfedge inside the face
	var next = null
	# the opposite halfedge
	var opposite = null

	var tmp: int = 0

class Face:
	extends Object

	# one of the halfedges bounding it
	var edge: Halfedge = null

	# flip the face direction
	func flip() -> void:
		var old_edge: Halfedge = edge
		var e0: Halfedge = edge
		var e1: Halfedge = edge.next
		var v0: Vertex = e0.vertex

		while true:
			var v_temp: Vertex = e1.vertex
			e1.vertex = v0
			v0 = v_temp

			var tmp: Halfedge = e1.next
			e1.next = e0
			if e1 == old_edge:
				break
			e0 = e1
			e1 = tmp

	func calculate() -> Dictionary:
		var e0: Halfedge = edge
		if e0 == null:
			return {
				"vertex_count": 0,
				"face_normal": Vector3(0, 0, 1),
				"face_center": Vector3(0, 0, 0),
				"halfedges": [],
			}

		var vertex_count: int = 1
		var face_normal: Vector3 = Vector3(0, 0, 0)
		var face_center: Vector3 = e0.vertex.pos
		var halfedges: Array = [e0]

		var prev_vector: Vector3

		var e: Halfedge = e0.next
		while e != e0:
			halfedges.push_back(e)
			vertex_count += 1

			var current_vector: Vector3 = e.vertex.pos - e0.vertex.pos
			if vertex_count >= 3:
				face_normal += prev_vector.cross(current_vector)
			prev_vector = current_vector

			face_center += e.vertex.pos

			e = e.next

		face_center /= vertex_count

		return {
			"vertex_count": vertex_count,
			"face_normal": face_normal,
			"face_center": face_center,
			"halfedges": halfedges,
		}

# clear all vertices, edges and faces
func clear() -> void:
	for o in _vertices:
		o.free()
	for o in _halfedges:
		o.free()
	for o in _faces:
		o.free()
	_vertices.clear()
	_halfedges.clear()
	_faces.clear()

# add a polyhedron
# vertices: array of Vector3: vertices
# smoothed_normal: array of Vector3: smoothed normals (optional)
# face_vertex_indices: array of array of int: face vertex indices
func add_polyhedron(vertices: Array, smoothed_normal: Array, face_vertex_indices: Array) -> Dictionary:
	var new_vertices: Array = []
	var new_faces: Array = []

	# create new vertices
	for i in range(vertices.size()):
		var v: Vertex = Vertex.new()
		v.pos = vertices[i] as Vector3
		if smoothed_normal != null and i < smoothed_normal.size():
			v.smoothed_normal = smoothed_normal[i] as Vector3
		new_vertices.push_back(v)

	# create new faces
	var edge_map: Dictionary = {}
	for i in face_vertex_indices:
		var face: Array = i as Array
		var m: int = face.size()
		if m >= 3:
			var f: Face = Face.new()
			var prev_index: int = face[face.size() - 1]
			var prev_edge: Halfedge = null

			for j in range(m):
				var index: int = face[j]

				var e: Halfedge = Halfedge.new()
				if j == 0:
					# init first edge
					f.edge = e
				else:
					# set next edge of prev edge
					prev_edge.next = e

				# set end vertex
				e.vertex = new_vertices[index]

				# set start vertex
				var prev_vertex: Vertex = new_vertices[prev_index]
				if prev_vertex.edge == null:
					prev_vertex.edge = e

				# set face
				e.face = f

				# set opposite edge
				edge_map[Vector2(prev_index, index)] = e
				var opposite: Halfedge = edge_map[Vector2(index, prev_index)]
				if opposite != null:
					e.opposite = opposite
					opposite.opposite = e

				# over
				_halfedges.push_back(e)
				prev_index = index
				prev_edge = e

			# set next edge of last edge
			prev_edge.next = f.edge

			# add new face
			new_faces.push_back(f)

	# add to existing data
	_vertices += new_vertices
	_faces += new_faces

	return {
		"new_vertices": new_vertices,
		"new_faces": new_faces,
	}

func _notification(what: int) -> void:
	match what:
		NOTIFICATION_PREDELETE:
			clear()
